import robot_control
import serial
import serial.tools.list_ports
import queue
from consts import *
import threading

class Serial_Communicator(robot_control.Base_Communicator):
    def __init__(self, port:str, baudrate=115200, signal_que=None):
        self.port = port
        self.baudrate = baudrate
        self.serial = serial.Serial(port, baudrate)
        self.serial.timeout = 0
        self.serial.write_timeout = 0
        self.rlock = threading.Lock()
        self.wlock = threading.Lock()
        self.signal_que = signal_que
        print(port)
    
    def __del__(self):
        self.close()

    def start(self):
        if not self.serial.is_open: 
            self.serial.open()
    
    def close(self):
        self.rlock.acquire()
        self.wlock.acquire()
        print(f"{self.port} closed")
        if self.serial.is_open:
            self.serial.close()
        self.rlock.release()
        self.wlock.release()
    
    def write(self, bytes_: bytes) -> int:
        num = 0
        self.wlock.acquire()
        if self.serial.is_open:
            # print(bytes_)
            num =  self.serial.write(bytes_)
        self.wlock.release()
        return num
    
    def read(self, length: int=None) -> bytes:
        msg = b''
        self.rlock.acquire()
        try:
            if self.serial.is_open:
                if length is not None:
                    msg = self.serial.read(length)
                else:
                    msg = self.serial.read_all()
        except Exception as e:
            raise e
        finally:
            self.rlock.release()
        return msg

class Serial_Manager():
    def __init__(self):
        self.ports = []
        self.names = []
        self.guique = None
    
    def scan_ports(self):
        options = serial.tools.list_ports.comports()
        ports = [i.device for i in options]
        names = [i.description for i in options]
        self.ports = ports
        self.names = names
        return ports, names

    def start_port(self, port: str) -> str:
        try:
            _s = serial.Serial(port, 115200)
            _s.close()
            return Serial_Communicator(port, signal_que=self.guique)
        except Exception as e:
            if self.guique is not None:
                self.guique.put(f"{SERIAL_CANNOT_OPEN} {e}")
            return robot_control.Base_Communicator()
            

    def set_gui_signal_que(self, gui_signal:queue.Queue):
        self.guique = gui_signal


